#include "llvm/Linker.h"
#include "llvm/LLVMContext.h"
#include "llvm/Module.h"
#include "llvm/System/Path.h"
#include "llvm/PassManager.h"
#include "llvm/Bitcode/ReaderWriter.h"
#include "llvm/Analysis/Verifier.h"
#include "llvm/Assembly/Parser.h"
#include "llvm/Assembly/PrintModulePass.h"

#include "llvm/Support/CommandLine.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/Streams.h"
#include "llvm/Support/SystemUtils.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/System/Signals.h"
#include <iostream>
#include <fstream>
#include <memory>

using namespace llvm;

static cl::opt<bool>
	Force("f", cl::desc("Overwrite output files"));

static cl::opt<bool>
	DontPrint("disable-output", cl::desc("Don't output the .ll file"), cl::Hidden);

static cl::opt<bool>
DumpAsm("d", cl::desc("Print assembly as parsed"), cl::Hidden);

static cl::opt<bool>
DisableVerify("disable-verify", cl::Hidden,
              cl::desc("Do not run verifier on input LLVM (dangerous!)"));

static cl::opt<bool>
Verbose("v", cl::desc("Print information about actions taken"));


// LoadFile - Read the specified bitcode file in and return it.  This routine
// searches the link path for the specified file to try to find it...
//
static inline std::auto_ptr<Module> LoadFile(const std::string &FN, 
                                             LLVMContext& Context) {
  sys::Path Filename;
  if (!Filename.set(FN)) {
    cerr << "Invalid file name: '" << FN << "'\n";
    return std::auto_ptr<Module>();
  }

  std::string ErrorMessage;
  if (Filename.exists()) {
    if (Verbose) cerr << "Loading '" << Filename.c_str() << "'\n";
    Module* Result = 0;
    
    const std::string &FNStr = Filename.toString();
    if (MemoryBuffer *Buffer = MemoryBuffer::getFileOrSTDIN(FNStr,
                                                            &ErrorMessage)) {
      Result = ParseBitcodeFile(Buffer, Context, &ErrorMessage);
      delete Buffer;
    }
    if (Result) return std::auto_ptr<Module>(Result);   // Load successful!

    if (Verbose) {
      cerr << "Error opening bitcode file: '" << Filename.c_str() << "'";
      if (ErrorMessage.size()) cerr << ": " << ErrorMessage;
      cerr << "\n";
    }
  } else {
    cerr << "Bitcode file: '" << Filename.c_str() << "' does not exist.\n";
  }

  return std::auto_ptr<Module>();
}

std::string next_file(std::string inputfile)
{
	static int cur_pos=0;
	unsigned int p=0;
	std::string cur_file;
	//cout<<inputfile.length()<<"	"<<cur_pos;
	for (p=cur_pos;p<inputfile.length();p++)
	{
		//cout<<p;
		if (inputfile[p]==' ') break;
	}
//	cout<<"	";
	cur_file.assign(inputfile,cur_pos,p-cur_pos);
	//cout<<cur_file<<endl;
	cur_pos=p+1;
	return cur_file;
}

int gen_finalfile(std::string bitcode_files,cl::opt<std::string> OutputFilename,int argc,char **argv)
{
	sys::PrintStackTraceOnErrorSignal();
  	PrettyStackTraceProgram X(argc, argv);
  
  	LLVMContext Context;
  	llvm_shutdown_obj Y;  // Call llvm_shutdown() on exit.
//  cl::ParseCommandLineOptions(argc, argv, "llvm linker\n");

  	//unsigned BaseArg = 0;
  	std::string ErrorMessage;

//	static cl::list<std::string> InputFilenames(cl::NormalFormatting,cl::OneOrMore,cl::desc("<input bitcode files>"));
	
//	bitcode_files;
	std::string cur_file;
	cur_file=next_file(bitcode_files);

  	std::auto_ptr<Module> Composite(LoadFile(cur_file, Context));
  	if (Composite.get() == 0) {
    		cerr << argv[0] << ": error loading file '"
    	     		<< cur_file << "'\n";
    		return 1;
  	}

	  for (unsigned i = 0; i < bitcode_files.length(); i+=cur_file.length()+1) {
		cout<<cur_file<<std::endl;		
		//
    		std::auto_ptr<Module> M(LoadFile(cur_file, Context));
    		if (M.get() == 0) {
      			cerr << argv[0] << ": error loading file '" <<cur_file<< "'\n";
      			return 1;
    		}

    	  	if (Verbose) cerr << "Linking in '" << cur_file << "'\n";

    		if (Linker::LinkModules(Composite.get(), M.get(), &ErrorMessage)) {
		      cerr << argv[0] << ": link error in '" << cur_file		
           		<< "': " << ErrorMessage << "\n";
      			return 1;
    		}
		cur_file=next_file(bitcode_files);	
  	}	
//  printf("OK\n");
  // TODO: Iterate over the -l list and link in any modules containing
  // global symbols that have not been resolved so far.

  if (DumpAsm) cerr << "Here's the assembly:\n" << *Composite.get();

  // FIXME: cout is not binary!
  std::ostream *Out = &std::cout;  // Default to printing to stdout...
  if (OutputFilename != "-") {
    if (!Force && std::ifstream(OutputFilename.c_str())) {
      // If force is not specified, make sure not to overwrite a file!
      cerr << argv[0] << ": error opening '" << OutputFilename
           << "': file exists!\n"
           << "Use -f command line argument to force output\n";
      return 1;
    }
    std::ios::openmode io_mode = std::ios::out | std::ios::trunc |
                                 std::ios::binary;
    Out = new std::ofstream(OutputFilename.c_str(), io_mode);
    if (!Out->good()) {
      cerr << argv[0] << ": error opening '" << OutputFilename << "'!\n";
      return 1;
    }

    // Make sure that the Out file gets unlinked from the disk if we get a
    // SIGINT
    sys::RemoveFileOnSignal(sys::Path(OutputFilename));
  }

  if (verifyModule(*Composite.get())) {
    cerr << argv[0] << ": linked module is broken!\n";
    return 1;
  }

  if (Verbose) cerr << "Writing bitcode...\n";
  WriteBitcodeToFile(Composite.get(), *Out);

  if (Out != &std::cout) delete Out;
	//printf("OK\n");	
	return 1;
}



int bc_generator(std::string InputFilename,std::string OutputFilename,int argc,char **argv)
{
  sys::PrintStackTraceOnErrorSignal();
  	
  PrettyStackTraceProgram X(argc, argv);

  LLVMContext Context;
  llvm_shutdown_obj Y;  // Call llvm_shutdown() on exit.

//  cl::ParseCommandLineOptions(argc, argv, "llvm .ll -> .bc assembler\n");

  int exitCode = 0;
  std::ostream *Out = 0;


  try {
    // Parse the file now...
    std::string ErrorStr;
	MemoryBuffer *F;
	F=MemoryBuffer::getFileOrSTDIN(InputFilename, &ErrorStr);
	return 1;
    SMDiagnostic Err;
    std::auto_ptr<Module> M(ParseAssemblyFile(InputFilename, Err, Context));
   
 //   return 1;	
    if (M.get() == 0) {
      Err.Print(argv[0], errs());
      return 1;
    }

    if (!DisableVerify) {
      std::string Err;
      if (verifyModule(*M.get(), ReturnStatusAction, &Err)) {
        cerr << argv[0]
             << ": assembly parsed, but does not verify as correct!\n";
        cerr << Err;
        return 1;
      } 
    }

    if (DumpAsm) cerr << "Here's the assembly:\n" << *M.get();

    if (OutputFilename != "") {   // Specified an output filename?
      if (OutputFilename != "-") {  // Not stdout?
        if (!Force && std::ifstream(OutputFilename.c_str())) {
          // If force is not specified, make sure not to overwrite a file!
          cerr << argv[0] << ": error opening '" << OutputFilename
               << "': file exists!\n"
               << "Use -f command line argument to force output\n";
          return 1;
        }
        Out = new std::ofstream(OutputFilename.c_str(), std::ios::out |
                                std::ios::trunc | std::ios::binary);
      } else {                      // Specified stdout
        // FIXME: cout is not binary!
        Out = &std::cout;
      }
    } else {
      if (InputFilename == "-") {
        OutputFilename = "-";
        Out = &std::cout;
      } else {
        std::string IFN = InputFilename;
        int Len = IFN.length();
        if (IFN[Len-3] == '.' && IFN[Len-2] == 'l' && IFN[Len-1] == 'l') {
          // Source ends in .ll
          OutputFilename = std::string(IFN.begin(), IFN.end()-3);
        } else {
          OutputFilename = IFN;   // Append a .bc to it
        }
        OutputFilename += ".o";

        if (!Force && std::ifstream(OutputFilename.c_str())) {
          // If force is not specified, make sure not to overwrite a file!
          cerr << argv[0] << ": error opening '" << OutputFilename
               << "': file exists!\n"
               << "Use -f command line argument to force output\n";
          return 1;
        }

        Out = new std::ofstream(OutputFilename.c_str(), std::ios::out |
                                std::ios::trunc | std::ios::binary);
        // Make sure that the Out file gets unlinked from the disk if we get a
        // SIGINT
        sys::RemoveFileOnSignal(sys::Path(OutputFilename));
      }
    }

    if (!Out->good()) {
      cerr << argv[0] << ": error opening " << OutputFilename << "!\n";
      return 1;
    }

    if (!DontPrint)
      if (Force || !CheckBitcodeOutputToConsole(Out,true))
        WriteBitcodeToFile(M.get(), *Out);
  } catch (const std::string& msg) {
    cerr << argv[0] << ": " << msg << "\n";
    exitCode = 1;
  } catch (...) {
    cerr << argv[0] << ": Unexpected unknown exception occurred.\n";
    exitCode = 1;
  }

  if (Out != &std::cout) delete Out;
  return exitCode;
}





std::string ass_generator(cl::opt<std::string> InputFilename,std::string OutputFilename,int argc, char **argv)
{
	sys::PrintStackTraceOnErrorSignal();
 	PrettyStackTraceProgram X(argc, argv);
  
  	LLVMContext Context;
  	llvm_shutdown_obj Y;  // Call llvm_shutdown() on exit.
  	try {
 	//   cl::ParseCommandLineOptions(argc, argv, "llvm .bc -> .ll disassembler\n");
	   std::ostream *Out = &std::cout;  // Default to printing to stdout.
           std::string ErrorMessage;

    	   std::auto_ptr<Module> M;
   
    	   if (MemoryBuffer *Buffer
                = MemoryBuffer::getFileOrSTDIN(InputFilename, &ErrorMessage)) 
	   {
             	M.reset(ParseBitcodeFile(Buffer, Context, &ErrorMessage));
      		delete Buffer;
    	   }

 	   if (M.get() == 0) {
 	     cerr << argv[0] << ": ";
 	     if (ErrorMessage.size())
 	        cerr << ErrorMessage << "\n";
	     else
        	cerr << "bitcode didn't read correctly.\n";
      	   return NULL;
    	   }
    
    	   if (DontPrint) {
      // Just use stdout.  We won't actually print anything on it.
    	   } 
	   else 
	   if (OutputFilename != "") 
	   {   // Specified an output filename?
      		if (OutputFilename != "-") { // Not stdout?
        		if (!Force && std::ifstream(OutputFilename.c_str())) 
			{
          		// If force is not specified, make sure not to overwrite a file!
          			cerr << argv[0] << ": error opening '" << OutputFilename
               				<< "': file exists! Sending to standard output.\n";
        		} 
			else
				Out = new std::ofstream(OutputFilename.c_str());
        		
      	  	 }//end if (OutFilename != "-")
    	    }//end if (OutputFilename != "")
    	    else 
   	    {
    	   	if (InputFilename == "-") 
    	    		OutputFilename = "-";
      	  	else 
	   	{
        		std::string IFN = InputFilename;
       		 	int Len = IFN.length();
        		if (IFN[Len-3] == '.' && IFN[Len-2] == 'b' && IFN[Len-1] == 'c') 
			{
          			// Source ends in .bc
          			OutputFilename = std::string(IFN.begin(), IFN.end()-3)+"_temp.gpu_asm";
        		} 
			else
			   	OutputFilename = IFN+"_temp.gpu_asm";

                	if (!Force && std::ifstream(OutputFilename.c_str())) {
        		  // If force is not specified, make sure not to overwrite a file!
       			   	cerr << argv[0] << ": error opening '" << OutputFilename
             			  << "': file exists! Sending to standard output.\n";
        		} 
			else
			{
        	 		Out = new std::ofstream(OutputFilename.c_str());
	
	          		// Make sure that the Out file gets unlinked from the disk if we get a
	          		// SIGINT
	          		sys::RemoveFileOnSignal(sys::Path(OutputFilename));
        		}
      	    	}//end else
    	      }//end else

    	      if (!Out->good()) {
      			cerr << argv[0] << ": error opening " << OutputFilename
           			<< ": sending to stdout instead!\n";
      			Out = &std::cout;
    	      }

    		// All that llvm-dis does is write the assembly to a file.
    	      if (!DontPrint) {
   	   		PassManager Passes;
     	   		raw_os_ostream L(*Out);
      	  	        Passes.add(createPrintModulePass(&L));
      	   		Passes.run(*M.get());
    	      }
	      if (Out != &std::cout) 
	      {
      			((std::ofstream*)Out)->close();
 			delete Out;
 	      }
    	  	//    return 0;
  	}//end try 
  	catch (const std::string& msg) {
   		 cerr << argv[0] << ": " << msg << "\n";
  	}catch (...) {
    		cerr << argv[0] << ": Unexpected unknown exception occurred.\n";
  	}
	return OutputFilename;
}	
